home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 7 / BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso / Files / Tele / C / Comet2.1.3.cpt / emlib / macro.c < prev    next >
Text File  |  1991-07-25  |  43KB  |  2,013 lines

  1. /*
  2.     Copyright Cornell University 1986.  All rights are reserved.
  3.     
  4.     macro.c routines handle scanning keys for macro matches,
  5.     presenting a dialog for users to associate macros with keys,
  6.     and dumping the key macros.
  7.     
  8.     For a list of valid macro codes, see ::include:rcodes.h.
  9. */
  10.  
  11.  
  12. #include <em.h>
  13.  
  14. #include <net.h>
  15. #include <h19.h>
  16. #include <rcodes.h>
  17. #include <resdefs.h>
  18.  
  19.  
  20. #define NEG (-1)
  21.  
  22. /*     each key on the keyboard has a unique name derived from this table,
  23.     which associates keycodes & titles */
  24.  
  25. #define KEYCODES 128
  26. char * keytitle[KEYCODES] = {
  27. "A",
  28. "S",
  29. "D",
  30. "F",
  31. "H",
  32. "G",
  33. "Z",
  34. "X",
  35. "C",
  36. "V",
  37. "?",/* 10 */
  38. "B",
  39. "Q",
  40. "W",
  41. "E",
  42. "R",
  43. "Y",
  44. "T",
  45. "1",
  46. "2",
  47. "3",/* 20 */
  48. "4",
  49. "6",
  50. "5",
  51. "=",
  52. "9",
  53. "7",
  54. "-",
  55. "8",
  56. "0",
  57. "]",/* 30 */
  58. "O",
  59. "U",
  60. "[",
  61. "I",
  62. "P",
  63. "Return",
  64. "L",
  65. "J",
  66. "'",
  67. "K",/* 40 */
  68. ";",
  69. "\\",
  70. ",",
  71. "/",
  72. "N",
  73. "M",
  74. ".",
  75. "Tab",
  76. "Space",
  77. "`",/* 50 */
  78. "Backspace/delete",
  79. "Enter",
  80. "esc",
  81. "?",
  82. "?",
  83. "?",
  84. "?",
  85. "?",
  86. "?",
  87. "?",/* 60 */
  88. "?",
  89. "?",
  90. "?",
  91. "?",
  92. ". (KP)",
  93. "* (KP)",
  94. "* (KP)",
  95. "?",
  96. "+ (KP)",
  97. "+ (KP)",/* 70 */
  98. "Clear (KP)",
  99. ", (KP)",
  100. "?",
  101. "?",
  102. "/ (KP)",
  103. "Enter (KP)",
  104. "/ (KP)",
  105. "- (KP)",
  106. "?",
  107. "?",/* 80 */
  108. "= (KP)",
  109. "0 (KP)",
  110. "1 (KP)",
  111. "2 (KP)",
  112. "3 (KP)",
  113. "4 (KP)",
  114. "5 (KP)",
  115. "6 (KP)",
  116. "7 (KP)",
  117. "?",/* 90 */
  118. "8 (KP)",
  119. "9 (KP)",
  120. "?",
  121. "?",
  122. "?", 
  123. "F5",
  124. "F6",
  125. "F7",
  126. "F3",
  127. "F8",    /* 100 */
  128. "F9",
  129. "?",
  130. "F11",
  131. "?",
  132. "F13",
  133. "?",
  134. "F14",
  135. "?",
  136. "F10",
  137. "?",    /* 110 */
  138. "F12",
  139. "?",
  140. "F15",
  141. "help",
  142. "home",
  143. "page up",
  144. "X->",
  145. "F4",
  146. "end",
  147. "F2",    /* 120 */
  148. "page down",
  149. "F1",
  150. "Left",
  151. "Right",
  152. "Down",
  153. "Up",
  154. "?"
  155. };
  156.  
  157. /* key macro mapping dialog defines */
  158.  
  159. #define DIBMKEYEXCEPT    11450
  160. #define DASCKEYEXCEPT    11451
  161.  
  162. #define DKEYNADA    0            /* do nothing at all */
  163. #define DKEYOK        1            /* OK button    */
  164. #define DKEYCANCEL    2            /* cancel button */
  165. #define    DKEYPRESS    3            /* do another key */
  166. #define DKEYDESC    4            /* text description of key */
  167.     /* skip */
  168. #define    DKEYACTION    6            /* action code has been changed */
  169. #define    DKEYCODE    7            /* the action code so user can see it */
  170.     /* skip */
  171. #define    DKEYKILL    9            /* remove key from macro list */
  172. #define    DKEYQUIT    10            /* quit this dialog */
  173. #define    DKEYHELP    11            /* show help window */
  174. #define    DKEYSHOW    12            /* show all the current key macros */
  175. #define    DKEYDISPLAY    1000            /* do the actual key display */
  176.     /* the range from 14 to MAXBUTACT is associated with buttons which produce the command strings for you */
  177. #define DPF1    14
  178. #define DPFACTS    24
  179. #define DACT1    (DPF1 + DPFACTS)
  180. #define MAXBUTACT    57
  181. #define NUMBUTACTS    MAXBUTACT - DACT1
  182.  
  183. #define ITEXTMACROS        2012
  184. #define ALKEYQUIT        0
  185.  
  186.  
  187. /* the list of icon codes -- some command codes can appear as a single icon 
  188.     in the "Ourcago" character set to ease editing */
  189.  
  190. #define IPF1    128
  191. #define IPF2    129
  192. #define IPF3    130
  193. #define IPF4    131
  194. #define IPF5    132
  195. #define IPF6    133
  196. #define IPF7    134
  197. #define IPF8    135
  198. #define IPF9    136
  199. #define IPF10    137
  200. #define IPF11    138
  201. #define IPF12    139
  202. #define IPF13    140
  203. #define IPF14    141
  204. #define IPF15    142
  205. #define IPF16    143
  206. #define IPF17    144
  207. #define IPF18    145
  208. #define IPF19    146
  209. #define IPF20    147
  210. #define IPF21    148
  211. #define IPF22    149
  212. #define IPF23    150
  213. #define IPF24    151
  214. #define IPA1    152
  215. #define IPA2    153
  216. #define IPA3    154
  217. #define IENTER    155
  218. #define INEWLINE    156
  219. #define ICLEAR    157
  220. #define IRESET    158
  221. #define IHOME    159
  222. #define ILEFT    160
  223. #define IUP        161
  224. #define IDOWN    162
  225. #define IRIGHT    163
  226. #define ITAB    164
  227. #define IBACKTAB    165
  228. #define IINSERT    166
  229. #define IDELETE    167
  230. #define IERINPUT    168
  231. #define IEREOF    169
  232. #define IBSDEL    170
  233.  
  234. char * outoftext = "Cannot display text--only 255 characters available";
  235. char * endoftext = "Invalid code at end of entry";
  236.  
  237. long keyid;                    /* our unique key id, keymods / keycode / keychar */
  238. short keycode;                /* key code */
  239. short keymods;                /* key modifiers */
  240. short KDnotshown = TRUE;    /* no key selected in key dialog, changes CR response */
  241. short doshow = FALSE;        /* show codes for next key */
  242. short macrochanged;            /* has a key action sequence been modified? */
  243.  
  244. char emptytext[2] = { '\000', '\000' }; 
  245. extern char * malloc();
  246.  
  247. DialogPtr    macrodp;
  248. DialogRecord macrodlog;
  249.  
  250. /* do dialog to examine and set key macro list action codes */
  251. Handle haction;                /* action text is global so KDkeymod can get it */
  252. Handle hdesc;                /* various dialog objects */
  253. Handle hnext;
  254. Handle hshow;
  255.  
  256. extern long memtest();
  257.  
  258.  
  259. setkeydialog()
  260. {
  261.     Rect rect, showrect;        /* rect is garbage, others are used */
  262.     Rect nextrect; 
  263.     short dialnum;
  264.     short type;
  265.     short item_id;
  266.     short count;
  267.     short nomacro;            /* is there an macro list for the current key? */
  268.     Str255 desctext, acttext;
  269.     Str255 nextprompt;
  270.     char * actptr;
  271.     struct token * keyact;
  272.     GrafPtr oport;
  273.     char temptext[512];        /* big array for action code translation */
  274.     short templength;
  275.     Rect offnextrect;        /* used to move "Press key" off-window */
  276.     char windtit[256];        /* temp for window title */
  277.  
  278.     extern Boolean KDkeymod();
  279.  
  280.     nomacro = TRUE;
  281.     SetDAFont(OURCHICAGO);    /* chicago font with control chars defined */
  282.     GetPort(&oport);
  283.  
  284.     dialnum = keydp->ibm_keymode ? DIBMKEYEXCEPT : DASCKEYEXCEPT;
  285.  
  286.     emwdeactivate();
  287.     if ((macrodp = GetNewDialog(dialnum, (Ptr) ¯odlog, (WindowPtr) (-1))) == NULL)
  288.         return(0);
  289.  
  290.     centerwind(macrodp);
  291.  
  292.     reopenconfig(keydp);
  293.     
  294.     showcursor();
  295.     SetCursor(&arrow);
  296.     SetPort(macrodp);
  297.  
  298.     GetDItem(macrodp, DKEYPRESS, &type, &hnext, &nextrect);
  299.     GetWTitle(keydp->emwindow, &windtit[0]);
  300.     SetIText(hnext, &windtit[0]);
  301.  
  302.     /* prompt now appears in key description box... */
  303.     GetDItem(macrodp, DKEYDESC, &type, &hdesc, &rect);
  304.     GetIText(hdesc, &nextprompt);
  305.  
  306.     GetDItem(macrodp, DKEYSHOW, &type, &hshow, &showrect);
  307.     GetDItem(macrodp, DKEYACTION, &type, &haction, &rect);
  308.  
  309.     keyshowprep(&nextprompt);
  310.  
  311.     TEFromScrap();
  312.         /* get the current scrap in case it's needed to put into the dialog text */
  313.         
  314.     while (TRUE) {
  315.         ModalDialog((ProcPtr) KDkeymod, &item_id);
  316.         SetPort(macrodp);
  317.         switch (item_id) {
  318.             case DKEYNADA: 
  319.                 break;
  320.             case DKEYOK: {
  321.                 struct keyxlist * keyxp;
  322.                 Handle hkeyres;
  323.  
  324.                 /* set key to use action codes user has entered */
  325.                 if (macrochanged) {
  326.                     /* get text  */
  327.                     GetIText(haction, &acttext);
  328.                     if ( (templength = actencode(&acttext, &temptext[0])) == NEG ) {
  329.                         /* encode into an action list, if bad abort ... */
  330.                         break;
  331.                     }
  332.                     if (templength % sizeof(struct token) ) {
  333.                         /* should be zero, or we've got a bad code ... abort */
  334.                         stoperror("Uneven number of codes"); 
  335.                         break;
  336.                     }
  337.  
  338.                     /* modify the resource */
  339.  
  340.                     /* add a resource when:
  341.                             1) a document file is open and an macro exists,
  342.                                 but in the appl resource file, so we will ignore it;
  343.                             2) no macro exists */
  344.                     if (!nomacro) {
  345.                         keyxp = keyxptr(keyid);
  346.                         if (!renewresource(keyxp))
  347.                             nomacro = TRUE;
  348.                         else if (keydp->resfid != appresfid && 
  349.                             appresfid == HomeResFile(keyxp->hkeyact)) {
  350.                             /* the key exists in the Settings file, override it */
  351.                             nomacro = TRUE;
  352.                         }
  353.                     }
  354.                         /* KS 8/27/86 */
  355.                     if (nomacro) {
  356.                         /* make a new resource */
  357.                         hkeyres = NewHandle((long) (templength + 4)); /* WARNING TODO should use sizeof keyactlist->keyid rather than hardcoded 4 */
  358.                         ((struct keyactlist *) *hkeyres)->keyid = keyid;
  359.                             /* set the key id field of the new resource */
  360.  
  361.                         keyxp = malloc((unsigned) sizeof(struct keyxlist));
  362.                         keyxp->hkeyact = hkeyres;
  363.                             /* set up a link */
  364.  
  365.                         keyxp->nextkey = keydp->keyxhead;
  366.                         keydp->keyxhead = keyxp;
  367.                             /* make the new link first in line */
  368.                     }
  369.                     else {
  370.                         hkeyres = keyxp->hkeyact;
  371.                         SetHandleSize(hkeyres, (long) (templength + 4)); /* WARNING TODO should use sizeof keyactlist->keyid rather than hardcoded 4 */
  372.                     }
  373.  
  374.                     keyxp->actlen = templength / sizeof(struct token);
  375.                     mystrncpy(&((struct keyactlist *) *hkeyres)->act[0], &temptext[0],
  376.                         templength);
  377.                         /* copy the new data over to the resource */
  378.  
  379.                     if (nomacro) {
  380.                         /* make a new KEYX resource */
  381.                         short newid;
  382.  
  383.                         newid = UniqueID(keydp->ibm_keymode ? 'KEYI' : 'KEYX');
  384.                         AddResource(hkeyres, keydp->ibm_keymode ? 'KEYI' : 'KEYX', 
  385.                             newid, &desctext);
  386.                     }
  387.  
  388.                     /* write out the resource */
  389.                     HNoPurge(hkeyres);
  390.                     ChangedResource(hkeyres);
  391.                     if (nomacro)
  392.                         UpdateResFile(keydp->resfid);
  393.                     else
  394.                         UpdateResFile(HomeResFile(hkeyres));
  395.                     if (ResError())
  396.                         error("Key resource save failed");
  397.  
  398.                     DetachResource(hkeyres);
  399.                 }
  400.                 keyshowprep(&nextprompt);
  401.                 break;
  402.             }
  403.             case DKEYSHOW: {
  404.                 keyxdump(OURCHICAGO, 12);
  405.                 break;
  406.             }
  407.             case DKEYDISPLAY: {
  408.                 /* do the display for a key combination */
  409.  
  410.                 /* unhighlite "Press Key" */
  411. #ifdef KEYPRESSCONTROL
  412.                 HiliteControl(hnext, 0);
  413. #else
  414.                 /* SetIText(hnext, "\P");  now in hdesc... */
  415. #endif
  416.                 /* give the key a name */
  417.                 namekey(desctext.text, keycode, keymods);
  418.                 desctext.length = strlen(desctext.text);
  419.                 SetIText(hdesc, &desctext);
  420.  
  421.                 if ( ((keyact = keyxfind(keyid, &count)) != NULL) ) {
  422.                     /* if key in macro list producing action keys, set action text */
  423.         /* TODO is the length restriction assoc. with Str255 acceptable  for actions? */
  424.                     char * textend;
  425.                     int length;
  426.  
  427.                     for ( textend = acttext.text + 254, actptr = acttext.text;  
  428.                             count--; keyact++) {
  429.                         /* copy text to pascal Str255 */
  430.                         if ( (length = actdecode(actptr, textend, keyact, TRUE)) == NEG)
  431.                             /* error decoding--should alert that at end? */
  432.                             break;
  433.                         else
  434.                             actptr += length;
  435.                     }
  436.                     acttext.length = actptr - acttext.text;
  437.                     SetIText(haction, &acttext);
  438.                     onditem(macrodp, DKEYKILL);
  439.                     nomacro = FALSE;
  440.                 }
  441.                 else {
  442.                     /* no macro yet exists */
  443.                     SetIText(haction, emptytext);
  444.                     nomacro = TRUE;
  445.                 }
  446.                 KDnotshown = FALSE;
  447.                 macrochanged = FALSE;
  448.                 onditem(macrodp, DKEYCANCEL);
  449.                 break;
  450.             }
  451.             case DKEYCANCEL: {
  452.                 /* do alert to see if cancel ok? */
  453.                 keyshowprep(&nextprompt);
  454.                 break;
  455.             }
  456.             case DKEYACTION: {
  457.                 if (!macrochanged) {
  458.                     offditem(macrodp, DKEYQUIT);
  459.                     onditem(macrodp, DKEYOK);
  460.                 }
  461.                 macrochanged = TRUE;
  462.                 break;
  463.             }
  464.             case DKEYKILL: {
  465.                 if (keyxkill(keyxptr(keyid))) {
  466.                     /* no such key in list */
  467.                     error("Key not in macro list");
  468.                 }
  469.                 keyshowprep(&nextprompt);
  470.                 break;
  471.             }
  472.             case DKEYQUIT: {
  473.                 SetPort(oport);
  474.                 ZeroScrap();
  475.                 TEToScrap();
  476.                     /* copy the TE scrap to the Scrap */
  477.                     
  478.                 CloseDialog(macrodp);
  479.                 SetDAFont(systemFont);    
  480.                 closeconfig(keydp);
  481.                 return(0);
  482.             }
  483.             case DKEYHELP: {
  484.                 helpwindow(ITEXTMACROS, geneva, 9);
  485.                 break;
  486.             }
  487.             default: {
  488.                 if (KDnotshown)
  489.                     error("You need to press a key-combination before you can enter macro buttons");
  490.                 else
  491.                     ibmacts(item_id);
  492.                 break;
  493.             }
  494.         }
  495.     }
  496. }
  497.  
  498.  
  499. pascal Boolean
  500. KDkeymod(dptr, devt, item)
  501. DialogPtr dptr;
  502. EventRecord * devt;
  503. short * item;
  504. {
  505.     short nkeycode;                /* key code */
  506.  
  507.     bkrd_service();
  508.     if (devt->what == keyDown) {
  509.         nkeycode = (devt->message & 0xff00) >> 8;
  510.  
  511.         if (doshow) {
  512.             /* the event is the key we want to display */
  513.             keymods = devt->modifiers;        /* global for key text */
  514.             keycode = nkeycode;
  515.  
  516.             /* derive the keyid from modifier << 16 + message */
  517.             keyid = devt->modifiers;
  518.             keyid <<= 16;
  519.             keyid |= (devt->message & 0xffff);
  520.             *item = DKEYDISPLAY;
  521.             doshow = FALSE;
  522.             return(TRUE);
  523.         }
  524.         if (KDnotshown) {
  525.             if ( (short) (devt->message & 0xff) == CR) {
  526.                 /* use Return to signal a Show */
  527.                 *item = DKEYSHOW;
  528.                 return(TRUE);
  529.             }
  530.             else {
  531.                 /* we're not accepting keyed input now */
  532.                 *item = DKEYCODE;
  533.                     /* any item that will be ignored will do */
  534.                 return(TRUE);
  535.             }
  536.         }
  537.         if (!(devt->modifiers & shiftKey) && !(devt->modifiers & cmdKey)
  538.             && ((devt->modifiers & optionKey && !(devt->modifiers & ctrlKey))
  539.                 || (devt->modifiers & ctrlKey && !(devt->modifiers & optionKey)))
  540.             ) {
  541.                 /* the Control or Option key is down all by itself */
  542.                 /* make a control char */
  543.             char contchar;
  544.  
  545.             contchar = (char) (devt->message & 0xff);
  546.             if (!makecontrol(&contchar, nkeycode)) {
  547.                 /* interpret as a control key */
  548.                 return(TRUE);
  549.             }
  550.             *item = DKEYACTION;    /* show we saw a key entered */
  551.             /* we courageously insert the char ourselves, since TE/Dialog won't reproduce correctly */
  552.             TEDelete(macrodlog.textH);
  553.                 /* kill select range as in standard */
  554.             TEInsert(&contchar, (long) 1, macrodlog.textH);
  555.             return(TRUE);
  556.         }
  557. #define MACROEDIT
  558. #ifdef MACROEDIT
  559.         if (devt->modifiers & cmdKey) {
  560.             /* do Cut Copy and Paste? */
  561.             char thechar;
  562.             
  563.             thechar = devt->message & 0xff;
  564.             switch (thechar) {
  565.                 case 'x': {
  566.                     TECut(macrodlog.textH);
  567.                     break;
  568.                 }
  569.                 case 'c': {
  570.                     TECopy(macrodlog.textH);
  571.                     break;
  572.                 }
  573.                 case 'v': {
  574.                     TEPaste(macrodlog.textH);
  575.                     break;
  576.                 }
  577.                 case '.': {
  578.                     /* cancel setting the key */
  579.                     *item = DKEYCANCEL;    
  580.                     return(TRUE);
  581.                 }
  582.             }
  583.             *item = DKEYACTION;    /* show we saw a key entered */
  584.             return(TRUE);
  585.         }
  586. #endif
  587.         /* TODO add test to distinguish CR & do a show button ? */
  588.         
  589.     }
  590.     else if (devt->what == mouseDown && !KDnotshown) {
  591.         /* pull down menus to insert codes if doing input and in menu area */
  592.         short code;
  593.         long menucode;
  594.         WindowPtr whichWindow;
  595.  
  596.         code = FindWindow(pass(devt->where), &whichWindow);
  597.         if (code == inMenuBar) {
  598.             menucode = MenuSelect(pass(devt->where));
  599.         }
  600.     }
  601.     return(FALSE);
  602. }
  603.  
  604.  
  605. /*    this routine returns a pointer to a keyname table entry */
  606.  
  607. char nullstring[] = "Unknown Key";
  608.  
  609. char *
  610. keyname(keycode)
  611. unsigned short keycode;
  612. {
  613.     if (keycode >= KEYCODES) {
  614.         return(nullstring);
  615.     }
  616.     return(keytitle[keycode]);
  617. }
  618.  
  619.  
  620. /*    Get all KEYX resources, which contain a keyid followed by a series of  
  621.     action codes,  and place them into an array of keyid/char pointers 
  622.     
  623.     a macro must always have an action handle!
  624.     
  625. */ 
  626.  
  627. keyxinit()
  628. {
  629.     short numkeyx;        /* number of macro entries in list of keys */
  630.     short count;
  631.     long keysize;
  632.     Handle keyhand;
  633.     struct keyxlist * keyxp;
  634.     struct keyxlist * prevkeyxp;
  635.  
  636.     numkeyx = Count1Resources(emdp->ibm_keymode ? 'KEYI' : 'KEYX');
  637.     if (numkeyx == 0) {
  638.         /* no macros exist, terminate list */
  639.         emdp->keyxhead = NULL;
  640.         return(0);
  641.     }
  642.     emdp->keyxhead = keyxp = malloc((unsigned) sizeof(struct keyxlist));
  643.     if (emdp->keyxhead == NULL)
  644.         return(-1);
  645.         
  646.     for (count = 1; count <= numkeyx; count++) {
  647.         keyxp->hkeyact = Get1IndResource(emdp->ibm_keymode ? 'KEYI' : 'KEYX', count);
  648.         
  649.         if (keyxp->hkeyact == NULL)
  650.             continue;
  651.  
  652.         DetachResource(keyxp->hkeyact);
  653.             /* detach the resource so it's not seen by other windows loading 
  654.                 macros ... */
  655.  
  656.         keyxp->actlen = ((short) GetHandleSize(keyxp->hkeyact) - 4) / sizeof(struct token);
  657.             /* TODO should use sizeof keyactlist->keyid rather than hardcoded 4 */
  658.  
  659.         if (count < numkeyx) {
  660.             keyxp->nextkey = malloc((unsigned) sizeof(struct keyxlist));
  661.                 /* allocate next keyxlist struct and set current key pointer to it */
  662.             if (keyxp->nextkey == NULL) {
  663.                 /* give up if we're running out of memory */
  664.                 break;
  665.             }
  666.             prevkeyxp = keyxp;
  667.             keyxp = keyxp->nextkey;
  668.         }
  669.     }
  670.     keyxp->nextkey = NULL;
  671.         /* terminate the list */
  672.     if (keyxp->hkeyact == NULL) {
  673.         /* couldn't manage to get a resource for the last malloc */
  674.         free(keyxp);
  675.         if (keyxp == emdp->keyxhead)
  676.             /* no macros at all */
  677.             emdp->keyxhead = NULL;
  678.         else
  679.             prevkeyxp->nextkey = NULL;
  680.                 /* fix the link before to reflect no more in q */
  681.     }
  682.     return(0);
  683. }
  684.  
  685.  
  686. /*    This routine returns a pointer to the action sequence associated with a key 
  687.     if no action sequence exists, it returns NULL  
  688.     */
  689.  
  690. struct keyxlist *
  691. keyxptr(keyid)
  692. long keyid;
  693. {
  694.     long keyxid;
  695.     struct keyxlist * keyxp;
  696.     
  697.     for (keyxp = keydp->keyxhead; keyxp != NULL; keyxp = keyxp->nextkey) {
  698.         keyxid = ((struct keyactlist *) (*keyxp->hkeyact))->keyid;
  699.         if (keyxid == keyid) {
  700.             /* we've got a match, return pointer to macro struct */
  701.             return((struct keyxlist *) keyxp);
  702.         }
  703.     }
  704.     return(NULL);
  705.         /* no match */
  706. }
  707.  
  708.  
  709. /* TODO should use keyxptr not keyid */
  710.  
  711. struct token *
  712. keyxfind(keyid, actcount)
  713. long keyid;
  714. short * actcount;
  715. {
  716.     long keyxid;
  717.     struct keyxlist * keyxp;
  718.     struct token * tokep;
  719.     
  720.     for (keyxp = keydp->keyxhead; keyxp != NULL; keyxp = keyxp->nextkey) {
  721.         keyxid = ((struct keyactlist *) (*(keyxp->hkeyact)))->keyid;
  722.         if (keyxid == keyid) {
  723.             /* we've got a match, return count and pointer to action */
  724.             *actcount = keyxp->actlen;
  725.             tokep = &((struct keyactlist *) *(keyxp->hkeyact))->act[0];
  726.             return(tokep);
  727.         }
  728.     }
  729.     return(NULL);
  730.         /* no match */
  731. }
  732.  
  733. /* release the current batch of key resources */
  734.  
  735. keyxrelease()
  736. {
  737.  
  738.     struct keyxlist * keyxp, * oldxp;
  739.     
  740.     for (keyxp = emdp->keyxhead; keyxp != NULL; ) {
  741.         /* go through list releasing all */
  742.         DisposHandle(keyxp->hkeyact);
  743.         oldxp = keyxp;
  744.         keyxp = keyxp->nextkey;
  745.         free(oldxp);
  746.     }
  747.     emdp->keyxhead = NULL;
  748. }
  749.  
  750. /*    remove a key macro entry from the linked list,
  751.     and remove the resource from the resource file
  752.     */
  753.     
  754. char * keyresfail = "Key resource kill failed";
  755.  
  756. keyxkill(keyp)
  757. struct keyxlist * keyp;
  758. {
  759.     struct keyxlist * killnext;
  760.     short homekeyres;
  761.     
  762.     if (keyp != NULL) {
  763.         /* delete resource */
  764.         if (renewresource(keyp)) {
  765.             homekeyres = HomeResFile(keyp->hkeyact);
  766.             RmveResource(keyp->hkeyact);
  767.             UpdateResFile(homekeyres);
  768.             if (ResError())
  769.                 error(keyresfail);
  770.         }
  771.         DisposHandle(keyp->hkeyact);
  772.  
  773.         if ( (killnext = keyp->nextkey) != NULL) {
  774.             /* we're in the front or middle of the list, delete the 
  775.                 list entry by moving *next* entry up and freeing *it* */
  776.             keyp->actlen = keyp->nextkey->actlen;
  777.             keyp->hkeyact = keyp->nextkey->hkeyact;
  778.             keyp->nextkey = keyp->nextkey->nextkey;
  779.             free(killnext);
  780.         }
  781.         else {
  782.             /* we're at the end of the list */
  783.             if ( (killnext = keydp->keyxhead) == keyp) {
  784.                 /* we're at the front too! */
  785.                 keydp->keyxhead = NULL;
  786.                 free(killnext);
  787.             }
  788.             else {
  789.                 /* go through the list of macros until previous link */
  790.                 for (; killnext->nextkey != keyp;  killnext = killnext->nextkey)
  791.                     ;
  792.                 killnext->nextkey = NULL; 
  793.                     /* terminate the list */
  794.                 free(keyp);
  795.                     /* kill the entry */
  796.             }
  797.         }
  798.         return(0);
  799.     }
  800.     return(NEG);
  801.         /* no match */
  802. }
  803.  
  804.  
  805.  
  806. char thisistext[] = "Keys in the macro list:\r\r";
  807. char moretext[] = "\rClick for more...";
  808. char endtext[] = "\rEnd of list, click to exit";
  809.  
  810. #define DUMPWIND 1111
  811. #define XTEXT    500
  812.  
  813. keyxdump(font, fontsize)
  814. short font;
  815. short fontsize;
  816. {
  817.     long length;
  818.     char thetext[XTEXT];
  819.     char spaces[8];
  820.     EventRecord anevent;
  821.     TEHandle texthand;
  822.     WindowPtr helpwind;
  823.     GrafPtr oldport;
  824.     Rect bounds;
  825.     struct keyxlist * keyxp;
  826.     long keyxid;
  827.     short dkeycode, modifiers;
  828.     char * actptr;
  829.     char * textend;
  830.     struct token * keyact;
  831.     int count;
  832.     
  833.     emwdeactivate();
  834.     if ( (helpwind = GetNewWindow(DUMPWIND, (Ptr) NULL, (WindowPtr) (-1)) ) == NULL)
  835.         return(-1);
  836.         
  837.     centerwind(helpwind);
  838.     
  839.     strcpy(&spaces[0], "     ");
  840.     
  841.     
  842.     GetPort(&oldport);
  843.     SetPort(helpwind);
  844.  
  845.     bounds.top = 6;
  846.     bounds.left = 6;
  847.     bounds.bottom = thePort->portRect.bottom - 6;
  848.     bounds.right = thePort->portRect.right - 6;
  849.     texthand = TENew(&bounds, &bounds);
  850.     
  851.     (*texthand)->txFont = font;
  852.     (*texthand)->txSize = fontsize;
  853.     TESetSelect( (long) 0, (long) (*texthand)->teLength, texthand);
  854.     TEInsert(thisistext, (long) strlen(thisistext), texthand);
  855.  
  856.     for (keyxp = keydp->keyxhead; keyxp != NULL; keyxp = keyxp->nextkey) {
  857.         keyxid = ((struct keyactlist *) (*keyxp->hkeyact))->keyid;
  858.  
  859.         modifiers = ((keyxid >> 16) & 0xffff);
  860.         dkeycode = (keyxid & 0xffff) >> 8;
  861.  
  862.         /* give the key a name */
  863.         TEInsert(&spaces[0], (long) strlen(spaces), texthand);
  864.         namekey(thetext, dkeycode, modifiers);
  865.         TEInsert(&thetext[0], (long) strlen(thetext), texthand);
  866.         TEInsert("--", (long) 2, texthand);
  867.         
  868.         /* now dump the codes for it */
  869.         count = keyxp->actlen;
  870.         keyact = &((struct keyactlist *) (*keyxp->hkeyact))->act[0];
  871.         for (textend = thetext + XTEXT, actptr = &thetext[0]; count--; keyact++) {
  872.             if ( (length = actdecode(actptr, textend, keyact, TRUE)) == NEG)
  873.                 /* error decoding--should alert that at end? */
  874.                 break;
  875.             else
  876.                 actptr += length;
  877.         }
  878.         length = actptr - &thetext[0];
  879.         if (length + (*texthand)->teLength > 32000) {
  880.             error("Text too long to display");
  881.             break;
  882.         }
  883.         if (! memtest(length, "to dump key macros"))
  884.             /* out of memory */
  885.             break;
  886.         TEInsert(&thetext[0], (long) length, texthand);
  887.  
  888.         TEKey(CR, texthand);
  889.         if ((*texthand)->selRect.top > bounds.bottom - (4 * fontsize)) {
  890.             /* at bottom of window */
  891.             TEInsert(moretext, (long) strlen(moretext), texthand);
  892.             while (!GetNextEvent(mDownMask, &anevent)) {
  893.                 /* wait till a mouseDown event occurs to exit, unless a key is hit */
  894.                 SystemTask();                /* keep this if/until we use WaitNextEvent */
  895.                 bkrd_service();
  896.                 if (EventAvail(keyDownMask, &anevent))
  897.                     /* don't take keystrokes off the queue */
  898.                     break;
  899.             }
  900.             TESetSelect( (long) 0,  (long) (*texthand)->teLength, texthand);
  901.             TEDelete(texthand);
  902.         }
  903.     }
  904.     TEInsert(endtext, (long) strlen(endtext), texthand);
  905.     while (!GetNextEvent(mDownMask, &anevent)) {
  906.         /* wait till a mouseDown event occurs to exit, unless a key is hit */
  907.         SystemTask();                /* keep this if/until we use WaitNextEvent */
  908.         bkrd_service();
  909.         if (EventAvail(keyDownMask, &anevent))
  910.             /* don't take keystrokes off the queue */
  911.             break;
  912.     }
  913.  
  914.     SetPort(oldport);
  915.     TEDispose(texthand);
  916.     DisposeWindow(helpwind);
  917. }
  918.  
  919.  
  920.  
  921. #define FIRSTCHAR    0x00
  922. #define LASTCHAR     0x7f
  923.  
  924. /*    decode an action sequence into text with ! marking commands & \ prefixing 
  925.     hex or decimal numbers
  926.     returns an int--the length of the new text
  927.     */
  928.  
  929. actdecode(textp, textend, theact, commandfont)
  930. unsigned char * textp;
  931. char * textend;
  932. struct token * theact;
  933. int commandfont;            
  934.     /* assume the font has Command symbols so not all chars > 127 available */
  935. {
  936.     register unsigned char thechar;
  937.     short command;
  938.     char * startp;
  939.     short numbered;    /* display entry numerically */
  940.  
  941.     startp = textp;
  942.     if ( (textp + 1) >= textend) {
  943.         error(outoftext);
  944.         return(NEG);
  945.     }
  946.     command = FALSE;
  947.     numbered = FALSE;
  948.     switch (theact->class) {
  949.         case RSLT_ASCI: {
  950.             /* leave out the RSLT_ASCI "space" command code in the text */
  951.             break;
  952.         }
  953.         case RSLT_DELAY:
  954.         case RSLT_LOOP:
  955.         case RSLT_MATCH:
  956.         case RSLT_SEL1:
  957.         case RSLT_SEL2:
  958.         case RSLT_SEL3:
  959.         case RSLT_SEL4:
  960.         case RSLT_WIND:
  961.         case RSLT_XCURS:
  962.         case RSLT_YCURS: {
  963.             /* use numbers for the entry description--fall through */
  964.             *textp++ = '!';
  965.             *textp++ = theact->class;
  966.             numbered = TRUE;
  967.             break;
  968.         }
  969.         case RSLT_PFKY: {
  970.             if (!commandfont) {
  971.                 /* always use command chars for the entry description--fall through */
  972.                 *textp++ = '!';
  973.                 *textp++ = theact->class;
  974.                 command = TRUE;
  975.                 break;
  976.             }
  977.             if ((theact->entry > PF24 && theact->entry <= PF36)) {
  978.                 /* use numbers for numbered pf keys above 24--fall through */
  979.                 numbered = TRUE;
  980.             }
  981.             else {
  982.                 /* use character button "icons" to represent */
  983.                 if (theact->entry >= PF1 && theact->entry <= PF24) {
  984.                     *textp++ = theact->entry + 127;
  985.                     return(1);
  986.                 }
  987.                 else switch(theact->entry) {
  988.                     case PA1: {
  989.                         *textp++ = IPA1;
  990.                         return(1);
  991.                     }
  992.                     case PA2: {
  993.                         *textp++ = IPA2;
  994.                         return(1);
  995.                     }
  996.                     case PA3: {
  997.                         *textp++ = IPA3;
  998.                         return(1);
  999.                     }
  1000.                     case ENTER: {
  1001.                         *textp++ = IENTER;
  1002.                         return(1);
  1003.                     }
  1004.                     case CLEAR: {
  1005.                         *textp++ = ICLEAR;
  1006.                         return(1);
  1007.                     }
  1008.                 }
  1009.                 /* else no icon for code */
  1010.                 /* put out a '!' before the command */
  1011.                 *textp++ = '!';
  1012.                 *textp++ = theact->class;
  1013.                 command = TRUE;
  1014.                 break;
  1015.             }
  1016.         }
  1017.         case RSLT_MVCR: {
  1018.             if (!commandfont) {
  1019.                 /* always use command chars for the entry description--fall through */
  1020.                 *textp++ = '!';
  1021.                 *textp++ = theact->class;
  1022.                 command = TRUE;
  1023.                 break;
  1024.             }
  1025.             /* use the iconic representation */
  1026.             switch (theact->entry) {
  1027.                 case LEFT_ARROW: {
  1028.                     *textp++ = ILEFT;
  1029.                     return(1);
  1030.                 }
  1031.                 case DOWN_ARROW : {
  1032.                     *textp++ = IDOWN;
  1033.                     return(1);
  1034.                 }
  1035.                 case UP_ARROW: {
  1036.                     *textp++ = IUP;
  1037.                     return(1);
  1038.                 }
  1039.                 case RIGHT_ARROW: {
  1040.                     *textp++ = IRIGHT;
  1041.                     return(1);
  1042.                 }
  1043.                 case HOME: {
  1044.                     *textp++ = IHOME;
  1045.                     return(1);
  1046.                 }
  1047.                 case TAB_FWD: {
  1048.                     *textp++ = ITAB;
  1049.                     return(1);
  1050.                 }
  1051.                 case BACK_TAB: {
  1052.                     *textp++ = IBACKTAB;
  1053.                     return(1);
  1054.                 }
  1055.                 case NEW_LINE: {
  1056.                     *textp++ = INEWLINE;
  1057.                     return(1);
  1058.                 }
  1059.             }
  1060.             /* else no icon for code */
  1061.             /* put out a '!' before the command */
  1062.             *textp++ = '!';
  1063.             *textp++ = theact->class;
  1064.             command = TRUE;
  1065.             break;
  1066.         }
  1067.         case RSLT_LCAC: {
  1068.             if (!commandfont) {
  1069.                 /* always use command chars for the entry description--fall through */
  1070.                 *textp++ = '!';
  1071.                 *textp++ = theact->class;
  1072.                 command = TRUE;
  1073.                 break;
  1074.             }
  1075.             /* use icons for local actions */
  1076.             switch(theact->entry) {
  1077.                 case INSRT: {
  1078.                     *textp++ = IINSERT;
  1079.                     return(1);
  1080.                 }
  1081.                 case DEL_CHAR: {
  1082.                     *textp++ = IDELETE;
  1083.                     return(1);
  1084.                 }
  1085.                 case ERASE_EOF: {
  1086.                     *textp++ = IEREOF;
  1087.                     return(1);
  1088.                 }
  1089.                 case INPUT_ERASE: {
  1090.                     *textp++ = IERINPUT;
  1091.                     return(1);
  1092.                 }
  1093.                 case BACKSP_DEL: {
  1094.                     *textp++ = IBSDEL;
  1095.                     return(1);
  1096.                 }
  1097.                 case RESET: {
  1098.                     *textp++ = IRESET;
  1099.                     return(1);
  1100.                 }
  1101.             }
  1102.             /* else no icon for code */
  1103.             /* put out a '!' before the command */
  1104.             *textp++ = '!';
  1105.             *textp++ = theact->class;
  1106.             command = TRUE;
  1107.             break;
  1108.         }
  1109.         default: {
  1110.             /* put out a '!' before the command */
  1111.             *textp++ = '!';
  1112.             *textp++ = theact->class;
  1113.                 /* since classes are recognizable chars, no problem decoding */
  1114.             command = TRUE;
  1115.             break;
  1116.         }
  1117.     }
  1118.     thechar = theact->entry;
  1119.     if ( !numbered && thechar >= FIRSTCHAR 
  1120.             && (commandfont ? thechar <= LASTCHAR : TRUE)
  1121.         ) {
  1122.         /* ordinary character with extended set, show it */
  1123.         if (thechar == '\\') {
  1124.             /* add an escape to an escape char */
  1125.             if ( (textp + 1) >= textend) {
  1126.                 error(outoftext);
  1127.                 return(NEG);
  1128.             }
  1129.             *textp++ = thechar;
  1130.             *textp++ = thechar;
  1131.             return(textp - startp);
  1132.         }
  1133.         if (!command && thechar == '!') {
  1134.             /* add an escape to an escape char UNLESS we're in a command sequence */
  1135.             if ( (textp + 1) >= textend) {
  1136.                 error(outoftext);
  1137.                 return(NEG);
  1138.             }
  1139.             *textp++ = thechar;
  1140.             *textp++ = thechar;
  1141.             return(textp - startp);
  1142.         }
  1143.         /* else */
  1144.         if (textp >= textend) {
  1145.             error(outoftext);
  1146.             return(NEG);
  1147.         }
  1148.         *textp++ = thechar;
  1149.     }
  1150.     else {
  1151.         /* make a decimal string */
  1152.         if ( (textp + 4) > textend) {
  1153.             error(outoftext);
  1154.             return(NEG);
  1155.         }
  1156.         *textp++ = '\\';
  1157.         /* was 
  1158.             sprintf(textp, "%3d", thechar);
  1159.             textp += 3;
  1160.             no leading zeroes included! 
  1161.         */
  1162.         dectrans(textp, thechar);
  1163.         textp += 3;
  1164.     }
  1165.     return(textp - startp);
  1166. }
  1167.  
  1168.  
  1169.  
  1170. /* this routine converts the text from the dialog from ! commands 
  1171.     and \000 decimal codes to plain tokens */
  1172.  
  1173. actencode(text255, textp)
  1174. Str255 * text255;
  1175. char * textp;
  1176. {
  1177.     unsigned char * srcp;
  1178.     unsigned char * destp;
  1179.     unsigned char * endp;
  1180.     unsigned char thechar;
  1181.     
  1182.     for (srcp = text255->text, destp = textp, endp = text255->text + text255->length;
  1183.             srcp < endp; destp++) {
  1184.         switch ((thechar = *srcp++)) {
  1185.             case '\\': {
  1186.                 /* hex number encountered */
  1187.                 *destp++ = RSLT_ASCI;
  1188.                 if (*srcp == '\\') {
  1189.                     /* this is an escaped escape char, make a '\' */
  1190.                     if (srcp < endp)
  1191.                         *destp = *srcp++;
  1192.                     break;
  1193.                 }
  1194.                 else if (decdecode(srcp, destp, endp)){
  1195.                     /* bad decimal code or length, position text cursor */
  1196.                     selbadtext(srcp - text255->text, 3);
  1197.                     return(NEG);
  1198.                 }
  1199.                 srcp += 3;
  1200.                     /* skip the three chars that have just been decoded */
  1201.                 break;
  1202.             }
  1203.             case '!': {
  1204.                 /* copy 2 literal command bytes */
  1205.  
  1206.                 if (*srcp == '!') {
  1207.                     /* 2 !'s:  literal '!', not a command */
  1208.                     if (srcp < endp) {
  1209.                         *destp++ = RSLT_ASCI;
  1210.                         *destp = *srcp++;
  1211.                     }
  1212.                     else {
  1213.                         error(endoftext);
  1214.                         return(NEG);
  1215.                     }
  1216.                     break;
  1217.                 }
  1218.  
  1219.                 /* do the first command byte */
  1220.                 if (*srcp == '\\') {
  1221.                     /* a decimal digit follows */
  1222.                     if (decdecode(++srcp, destp, endp)){
  1223.                         selbadtext(srcp - text255->text, 3);
  1224.                         return(NEG);
  1225.                     }
  1226.                     srcp += 3;
  1227.                 }
  1228.                 else {
  1229.                     /* an ordinary byte length command field follows */
  1230.                     if (srcp < endp)
  1231.                         *destp = *srcp++;
  1232.                     else {
  1233.                         error(endoftext);
  1234.                         return(NEG);
  1235.                     }
  1236.                 }
  1237.                 destp++;
  1238.  
  1239.                 /* do the second command byte */
  1240.                 if (*srcp == '\\') {
  1241.                     /* a decimal digit follows */
  1242.                     if (decdecode(++srcp, destp, endp)) {
  1243.                         selbadtext(srcp - text255->text, 3);
  1244.                         return(NEG);
  1245.                     }
  1246.                     srcp += 3;
  1247.                 }
  1248.                 else {
  1249.                     if (srcp < endp)
  1250.                         *destp = *srcp++;
  1251.                     else {
  1252.                         error(endoftext);
  1253.                         return(NEG);
  1254.                     }
  1255.                 }
  1256.                 break;
  1257.             }
  1258.             /* and now we have a slew of iconic representations... */
  1259.             case IPF1:
  1260.             case IPF2:
  1261.             case IPF3:
  1262.             case IPF4:
  1263.             case IPF5:
  1264.             case IPF6:
  1265.             case IPF7:
  1266.             case IPF8:
  1267.             case IPF9:
  1268.             case IPF10:
  1269.             case IPF11:
  1270.             case IPF12:
  1271.             case IPF13:
  1272.             case IPF14:
  1273.             case IPF15:
  1274.             case IPF16:
  1275.             case IPF17:
  1276.             case IPF18:
  1277.             case IPF19:
  1278.             case IPF20:
  1279.             case IPF21:
  1280.             case IPF22:
  1281.             case IPF23:
  1282.             case IPF24: {
  1283.                 /* iconic PF keys */
  1284.                 *destp++ = RSLT_PFKY;
  1285.                 *destp = thechar - 127;        /* from 128 -> 1 */
  1286.                 break;
  1287.             }
  1288.             case IPA1: {
  1289.                 *destp++ = RSLT_PFKY;
  1290.                 *destp = PA1;
  1291.                 break;
  1292.             }
  1293.             case IPA2: {
  1294.                 *destp++ = RSLT_PFKY;
  1295.                 *destp = PA2;
  1296.                 break;
  1297.             }
  1298.             case IPA3: {
  1299.                 *destp++ = RSLT_PFKY;
  1300.                 *destp = PA3;
  1301.                 break;
  1302.             }
  1303.             case IENTER: {
  1304.                 *destp++ = RSLT_PFKY;
  1305.                 *destp = ENTER;
  1306.                 break;
  1307.             }
  1308.             case INEWLINE: {
  1309.                 *destp++ = RSLT_MVCR;
  1310.                 *destp = NEW_LINE;
  1311.                 break;
  1312.             }
  1313.             case ICLEAR: {
  1314.                 *destp++ = RSLT_PFKY;
  1315.                 *destp = CLEAR;
  1316.                 break;
  1317.             }
  1318.             case IRESET: {
  1319.                 *destp++ = RSLT_LCAC;
  1320.                 *destp = RESET;
  1321.                 break;
  1322.             }
  1323.             case IHOME: {
  1324.                 *destp++ = RSLT_MVCR;
  1325.                 *destp = HOME;
  1326.                 break;
  1327.             }
  1328.             case ILEFT: {
  1329.                 *destp++ = RSLT_MVCR;
  1330.                 *destp = LEFT_ARROW;
  1331.                 break;
  1332.             }
  1333.             case IUP: {
  1334.                 *destp++ = RSLT_MVCR;
  1335.                 *destp = UP_ARROW;
  1336.                 break;
  1337.             }
  1338.             case IDOWN: {
  1339.                 *destp++ = RSLT_MVCR;
  1340.                 *destp = DOWN_ARROW;
  1341.                 break;
  1342.             }
  1343.             case IRIGHT: {
  1344.                 *destp++ = RSLT_MVCR;
  1345.                 *destp = RIGHT_ARROW;
  1346.                 break;
  1347.             }
  1348.             case ITAB: {
  1349.                 *destp++ = RSLT_MVCR;
  1350.                 *destp = TAB_FWD;
  1351.                 break;
  1352.             }
  1353.             case IBACKTAB: {
  1354.                 *destp++ = RSLT_MVCR;
  1355.                 *destp = BACK_TAB;
  1356.                 break;
  1357.             }
  1358.             case IINSERT: {
  1359.                 *destp++ = RSLT_LCAC;
  1360.                 *destp = INSRT;
  1361.                 break;
  1362.             }
  1363.             case IDELETE: {
  1364.                 *destp++ = RSLT_LCAC;
  1365.                 *destp = DEL_CHAR;
  1366.                 break;
  1367.             }
  1368.             case IBSDEL: {
  1369.                 *destp++ = RSLT_LCAC;
  1370.                 *destp = BACKSP_DEL;
  1371.                 break;
  1372.             }
  1373.             case IERINPUT: {
  1374.                 *destp++ = RSLT_LCAC;
  1375.                 *destp = INPUT_ERASE;
  1376.                 break;
  1377.             }
  1378.             case IEREOF: {
  1379.                 *destp++ = RSLT_LCAC;
  1380.                 *destp = ERASE_EOF;
  1381.                 break;
  1382.             }
  1383.             default: {
  1384.                 /* ordinary character found, add proper action code prefix */
  1385. #ifdef MAGICCR
  1386.                 /* not such a good idea due to modem relationships... */
  1387.                 if (keydp->ibm_keymode && (thechar == CR)) {
  1388.                     /* translate CR to Enter in ibm_keymode */
  1389.                     *destp++ = RSLT_PFKY;
  1390.                     *destp = ENTER;
  1391.                 }
  1392.                 else
  1393. #endif
  1394.                 {
  1395.                     *destp++ = RSLT_ASCI;
  1396.                     *destp = thechar;
  1397.                 }
  1398.                 break;
  1399.             }
  1400.         }
  1401. #ifdef VERIFY
  1402.         if (tokenverify((struct token *) destp - 1))
  1403.             /* check the token */
  1404.             return(NEG);
  1405. #endif
  1406.     }
  1407.     return(destp - textp);
  1408. }
  1409.  
  1410.  
  1411.  
  1412. /* a version of strncpy that respects NULS and treats them as equals! */
  1413. /* LIBERATE THE NUL SET! */
  1414. /* NUL is BEAUTIFUL! */
  1415.  
  1416. mystrncpy(dest, source, length)
  1417. char * dest;
  1418. char * source;
  1419. short length;
  1420. {
  1421.     length++;
  1422.     while (--length) {
  1423.         *dest++ = *source++;
  1424.     }
  1425. }
  1426.  
  1427.  
  1428. char
  1429. mkcontrol(thechar)
  1430. char thechar;
  1431. {
  1432.     switch (thechar) {
  1433.         case '-':
  1434.         case '_': {
  1435.             /* make a US */
  1436.             return(0x1f);
  1437.         }
  1438.         case '6':
  1439.         case '^': {
  1440.             /* make an RS */
  1441.             return(0x1e);
  1442.         }
  1443.         case '2':
  1444.         case '@': {
  1445.             /* make a NUL */
  1446.             return(NUL);
  1447.         }
  1448.         case '/':
  1449.         case '?': {
  1450.             /* make a DEL */
  1451.             return(DEL);
  1452.         }
  1453.         case BS: {
  1454.             if (keydp->ba_bs)
  1455.                 /* convert to delete */
  1456.                 return(DEL);
  1457.             else
  1458.                 return(BS);
  1459.         }
  1460.         default: {
  1461.             if ( (thechar >= 'a' && thechar <= 'z')
  1462.                     || (thechar >= '[' && thechar <= '_') )
  1463.                 return( (thechar &= 0x1f) );
  1464.             else
  1465.                 return(NEG);
  1466.         }
  1467.     }
  1468. }
  1469.  
  1470. decval(strp, strlength)
  1471. register unsigned char * strp;
  1472. int strlength;
  1473. {
  1474.     register unsigned char * endp;
  1475.     int theval;
  1476.     int placeval;
  1477.     int place;
  1478.  
  1479.     endp = strp + strlength;
  1480.     place = 1;
  1481.     for (theval = 0; --endp >= strp; place *= 10) {
  1482.         /* calculate the number from the right to the left */
  1483.         if ( (*endp >= '0') && (*endp <= '9') ) {
  1484.             /* straightforward decimal number */
  1485.             placeval = *endp - '0';
  1486.         }
  1487.         else {
  1488.             error("Bad Decimal code found--\nnn needed (n = 0-9)");
  1489.             return(NEG);
  1490.         }
  1491.         theval += placeval * place;
  1492.     }
  1493.     return(theval);
  1494. }
  1495.  
  1496.  
  1497. /*    decodes a 3 character decimal string if enough src left, placing result in *destp */
  1498.  
  1499. decdecode(srcp, destp, endp)
  1500. char * srcp;
  1501. char * destp;
  1502. char * endp;
  1503. {
  1504.     if (srcp + 2 < endp ) {
  1505.         /* not at end of text */
  1506.         int dechold;
  1507.  
  1508.         if ( (dechold = decval(srcp, 3) ) == NEG) {
  1509.             /* bad dec code found */
  1510.             return(NEG);
  1511.         }
  1512.         else if (dechold < 0 || dechold > 255) {
  1513.             /* too large for char, size of entry */
  1514.             error("Decimal code must be less than \256");
  1515.             return(NEG);
  1516.         }
  1517.         *destp = dechold;
  1518.         return(0);
  1519.     }
  1520.     else {
  1521.         error(endoftext);
  1522.         return(NEG);
  1523.     }
  1524. }
  1525.  
  1526.  
  1527. buthilite(therect)
  1528. Rect * therect;
  1529. {
  1530.     Rect cprect;
  1531.  
  1532.     cprect.top = therect->top;
  1533.     cprect.left = therect->left;
  1534.     cprect.bottom = therect->bottom;
  1535.     cprect.right = therect->right;
  1536.  
  1537.     PenSize(3, 3);
  1538.     InsetRect(&cprect, -4, -4);
  1539.     FrameRoundRect(&cprect, 16, 16);
  1540.     PenSize(1, 1);
  1541. }
  1542.  
  1543.  
  1544. onditem(dptr, theitem)
  1545. DialogPtr dptr;
  1546. short theitem;
  1547. {
  1548.     short type; 
  1549.     Handle hitem;
  1550.     Rect box;
  1551.  
  1552.     GetDItem(dptr, theitem, &type, &hitem, &box);
  1553.     HiliteControl(hitem, 0);
  1554. }
  1555.  
  1556.  
  1557. offditem(dptr, theitem)
  1558. DialogPtr dptr;
  1559. short theitem;
  1560. {
  1561.     short type; 
  1562.     Handle hitem;
  1563.     Rect box;
  1564.  
  1565.     GetDItem(dptr, theitem, &type, &hitem, &box);
  1566.     HiliteControl(hitem, 255);
  1567. }
  1568.  
  1569.  
  1570. togglemacros()
  1571. {
  1572.     keydp->dokeymacros = !keydp->dokeymacros;
  1573.     getcontext(keydp);        /* set window */
  1574.     if (keydp->dokeymacros)
  1575.         showmacroflag();
  1576.     else
  1577.         clrmacroflag();
  1578. }
  1579.  
  1580.  
  1581. namekey(strp, keycode, modifiers)
  1582. char * strp;
  1583. short keycode;
  1584. short modifiers;
  1585. {
  1586.     sprintf(strp, "%s%s%s%s%s%s %s", 
  1587.         !(modifiers & btnState) ? "Alt" : "" ,        /* mouse down, alt state */
  1588.         modifiers & alphaLock ? "CapsLock" : "" ,
  1589.         modifiers & shiftKey ? " Shift" : "", 
  1590.         modifiers & optionKey ? " Option" : "" ,
  1591.         modifiers & cmdKey ? " Command" : "" ,
  1592.         modifiers & ctrlKey ? " Control" : "" ,
  1593.         keyname(keycode)
  1594.     ); 
  1595. }
  1596.  
  1597. char buttab[NUMBUTACTS] = {
  1598.     IENTER,
  1599.     INEWLINE,
  1600.     IPA1,
  1601.     IPA2,
  1602.     IPA3,
  1603.     ICLEAR,
  1604.     IRESET,
  1605.     IHOME,
  1606.     IINSERT,
  1607.     IDELETE,
  1608.     IERINPUT,
  1609.     IEREOF,
  1610.     ILEFT,
  1611.     IUP,
  1612.     IDOWN,
  1613.     IRIGHT,
  1614.     ITAB,
  1615.     IBACKTAB,
  1616.     IBSDEL
  1617. };
  1618.  
  1619.  
  1620.  
  1621. /* post a string of events that wind up in the text window */
  1622.  
  1623. ibmacts(butid)
  1624. int butid;
  1625. {
  1626.     register char * butstr;
  1627.  
  1628.     if (butid >= MAXBUTACT)
  1629.         return(NEG);
  1630.     if (butid < DACT1) {
  1631.         putkeyevt((char) (butid - DPF1 + 128));
  1632.         return();
  1633.     }
  1634.     putkeyevt(buttab[butid - DACT1]);
  1635. }
  1636.  
  1637.  
  1638.  
  1639. putkeyevt(thechar)
  1640. char thechar;
  1641. {
  1642.     long keymess;
  1643.  
  1644.     PostEvent(keyDown, (long) thechar);
  1645. }
  1646.  
  1647.  
  1648. selbadtext(start, count)
  1649. int start;
  1650. int count;
  1651. {
  1652.     SelIText(macrodp, DKEYACTION, (short) start, (short) (start + count));
  1653. }
  1654.  
  1655. /* silly routine to convert numeric char to decimal character representation w/ leading zeroes */
  1656.  
  1657. dectrans(thep, thechar)
  1658. char * thep;
  1659. unsigned char thechar;
  1660. {
  1661.     int remainder;
  1662.  
  1663.     *thep++ = thechar / 100 + '0';
  1664.     remainder = thechar % 100;
  1665.     *thep++ = (remainder / 10) + '0';
  1666.     remainder = remainder % 10;
  1667.     *thep++ = remainder + '0';
  1668. }
  1669.  
  1670.  
  1671. /* set things up to get a key to show */
  1672.  
  1673. keyshowprep(theprompt)
  1674. Str255 * theprompt;
  1675. {
  1676.     KDnotshown = TRUE;
  1677.     doshow = TRUE;
  1678.     macrochanged = FALSE;
  1679.  
  1680.     SetIText(hdesc, emptytext);
  1681.     SetIText(haction, emptytext);
  1682. #ifdef KEYPRESSCONTROL
  1683.     HiliteControl(hnext, 1);        /* Press Key control */
  1684. #else
  1685.     SetIText(hdesc, theprompt);
  1686. #endif
  1687.     offditem(macrodp, DKEYOK);
  1688.     offditem(macrodp, DKEYCANCEL);
  1689.     offditem(macrodp, DKEYKILL);
  1690.     onditem(macrodp, DKEYQUIT);
  1691. }
  1692.  
  1693.  
  1694. /* get back the resource for a given key */
  1695.  
  1696. renewresource(keyxp)
  1697. struct keyxlist * keyxp;
  1698. {
  1699.     long keyxid;
  1700.     short dkeycode, modifiers;
  1701.     char thetext[255];
  1702.  
  1703.     /* get the resource back again to delete it from the resource file */
  1704.     keyxid = ((struct keyactlist *) (*keyxp->hkeyact))->keyid;
  1705.  
  1706.     DisposHandle(keyxp->hkeyact);
  1707.     /* drop the current detached copy */
  1708.  
  1709.     modifiers = ((keyxid >> 16) & 0xffff);
  1710.     dkeycode = (keyxid & 0xffff) >> 8;
  1711.  
  1712.     namekey(&thetext[0], dkeycode, modifiers);
  1713.     ctop(&thetext[0]);
  1714.     keyxp->hkeyact = GetNamedResource(keydp->ibm_keymode ? 'KEYI' : 'KEYX', &thetext[0]);
  1715.     if (keyxp->hkeyact == NULL) {
  1716.         return(FALSE);
  1717.     }
  1718.     return(TRUE);
  1719. }
  1720.  
  1721. /* run a dialog to set a macro object */
  1722.  
  1723. Handle setmacro(macrotype, macroid, macroname)
  1724. unsigned long macrotype;    /* resource type i.e. 'CONN' */
  1725. short macroid;                /* resource # */
  1726. char * macroname;            /* user level name for the object */
  1727. {
  1728.     Rect rect, nextrect, showrect;        /* rect is garbage, others are used */
  1729.     short dialnum;
  1730.     short type;
  1731.     short item_id;
  1732.     short count;
  1733.     short nomacro;            /* is there an macro list for the current key? */
  1734.     Str255 desctext, acttext, nextprompt;
  1735.     char * actptr;
  1736.     struct token * macrop;
  1737.     GrafPtr oport;
  1738.     char temptext[512];        /* big array for action code translation */
  1739.     short templength;
  1740.     Rect offnextrect;        /* used to move "Press key" off-window */
  1741.     Handle hmacro;
  1742.     short quitdialog = FALSE;
  1743.     char windtit[256];        /* temp for window title */
  1744.     
  1745.     extern Boolean Dsetmacro();
  1746.  
  1747.     nomacro = TRUE;
  1748.     SetDAFont(OURCHICAGO);    /* chicago font with control chars defined */
  1749.     GetPort(&oport);
  1750.  
  1751.     dialnum = keydp->ibm_keymode ? DIBMKEYEXCEPT : DASCKEYEXCEPT;
  1752.  
  1753.     reopenconfig(keydp);
  1754.     hmacro = Get1Resource(macrotype, macroid);
  1755.  
  1756.     if ((macrodp = GetNewDialog(dialnum, (Ptr) ¯odlog, (WindowPtr) (-1))) == NULL) {
  1757.         closeconfig(keydp);
  1758.         return(hmacro);
  1759.     }
  1760.  
  1761.     emwdeactivate();
  1762.  
  1763.     centerwind(macrodp);
  1764.  
  1765.     showcursor();
  1766.     SetCursor(&arrow);
  1767.     SetPort(macrodp);
  1768.  
  1769.     GetDItem(macrodp, DKEYPRESS, &type, &hnext, &nextrect);
  1770.     GetWTitle(keydp->emwindow, &windtit[0]);
  1771.     SetIText(hnext, &windtit[0]);
  1772.  
  1773.     GetDItem(macrodp, DKEYDESC, &type, &hdesc, &rect);
  1774.     GetDItem(macrodp, DKEYSHOW, &type, &hshow, &showrect);
  1775.     GetDItem(macrodp, DKEYACTION, &type, &haction, &rect);
  1776.  
  1777.     /* do the display for the macro */
  1778.  
  1779.     /* set the macro's user name */
  1780.  
  1781.     ctop(macroname);
  1782.     SetIText(hdesc, macroname);
  1783.     
  1784.     if ( (hmacro != NULL) ) {
  1785.         /* if key in macro list producing action keys, set action text */
  1786.         /* TODO is the length restriction assoc. with Str255 acceptable  for actions? */
  1787.         char * textend;
  1788.         int length;
  1789.         
  1790.         HLock(hmacro);
  1791.         macrop = (struct token *) (*hmacro + 4);
  1792.         count = (GetHandleSize(hmacro) - 4) / 2;
  1793.         for ( textend = acttext.text + 254, actptr = acttext.text;  
  1794.                 count--; macrop++) {
  1795.             /* copy text to pascal Str255 */
  1796.             if ( (length = actdecode(actptr, textend, macrop, TRUE)) == NEG)
  1797.                 /* error decoding--should alert that at end? */
  1798.                 break;
  1799.             else
  1800.                 actptr += length;
  1801.         }
  1802.         acttext.length = actptr - acttext.text;
  1803.         SetIText(haction, &acttext);
  1804.         onditem(macrodp, DKEYKILL);
  1805.         nomacro = FALSE;
  1806.         HUnlock(hmacro);
  1807.     }
  1808.     else {
  1809.         /* no macro yet exists */
  1810.         SetIText(haction, emptytext);
  1811.         offditem(macrodp, DKEYKILL);
  1812.         nomacro = TRUE;
  1813.     }
  1814.  
  1815.     macrochanged = FALSE;
  1816.     offditem(macrodp, DKEYSHOW);
  1817.     offditem(macrodp, DKEYQUIT);
  1818.     onditem(macrodp, DKEYHELP);
  1819.     onditem(macrodp, DKEYCANCEL);
  1820.     onditem(macrodp, DKEYOK);
  1821.  
  1822.     TEFromScrap();
  1823.         /* get the current scrap in case it's needed to put into the dialog text */
  1824.         
  1825.     while (TRUE) {
  1826.         ModalDialog((ProcPtr) Dsetmacro, &item_id);
  1827.         SetPort(macrodp);
  1828.         switch (item_id) {
  1829.             case DKEYNADA:  {
  1830.                 break;
  1831.             }
  1832.             case DKEYCANCEL: {
  1833.                 /* do alert to see if cancel ok? */
  1834.                 quitdialog = TRUE;
  1835.                 break;
  1836.             }
  1837.             case DKEYOK: {
  1838.                 /* set macro object to use action codes user has entered */
  1839.                 if (macrochanged) {
  1840.                     /* get text  */
  1841.                     GetIText(haction, &acttext);
  1842.                     if ( (templength = actencode(&acttext, &temptext[0])) == NEG ) {
  1843.                         /* encode into an action list, if bad abort ... */
  1844.                         break;
  1845.                     }
  1846.                     if (templength % sizeof(struct token) ) {
  1847.                         /* should be zero, or we've got a bad code ... abort */
  1848.                         stoperror("Uneven number of codes"); 
  1849.                         break;
  1850.                     }
  1851.  
  1852.                     /* modify the resource */
  1853.  
  1854.                     /* add a resource whenno macro exists */
  1855.                     if (nomacro) {
  1856.                         /* make a new resource */
  1857.                         hmacro = NewHandle((long) (templength + 4)); 
  1858.                         ((struct keyactlist *) *hmacro)->keyid = 0;
  1859.                             /* set the key id field of the new macro resource to 0*/
  1860.  
  1861.                     }
  1862.                     else {
  1863.                         SetHandleSize(hmacro, (long) (templength + 4));
  1864.                     }
  1865.  
  1866.                     mystrncpy(&((struct keyactlist *) *hmacro)->act[0], &temptext[0],
  1867.                         templength);
  1868.                         /* copy the new data over to the resource */
  1869.  
  1870.                     if (nomacro) {
  1871.                         /* make a new resource */
  1872.                         AddResource(hmacro, macrotype, macroid, macroname);
  1873.                     }
  1874.  
  1875.                     /* write out the resource */
  1876.                     HNoPurge(hmacro);
  1877.                     ChangedResource(hmacro);
  1878.                     UpdateResFile(keydp->resfid);
  1879.                     if (ResError())
  1880.                         error("Macro resource save failed");
  1881.                 
  1882.                 }
  1883.                 quitdialog = TRUE;
  1884.                 break;
  1885.             }
  1886.             case DKEYKILL: {
  1887.                 RmveResource(hmacro);
  1888.                 UpdateResFile(keydp->resfid);
  1889.                 if (ResError())
  1890.                     error("Can't delete macro resource");
  1891.                 DisposHandle(hmacro);
  1892.                 hmacro = NULL;
  1893.                 
  1894.                 quitdialog = TRUE;
  1895.                 break;
  1896.             }
  1897.             case DKEYACTION: {
  1898.                 if (!macrochanged) {
  1899.                     offditem(macrodp, DKEYQUIT);
  1900.                     onditem(macrodp, DKEYOK);
  1901.                 }
  1902.                 macrochanged = TRUE;
  1903.                 break;
  1904.             }
  1905.             case DKEYHELP: {
  1906.                 helpwindow(ITEXTMACROS, geneva, 9);
  1907.                 break;
  1908.             }
  1909.             default: {
  1910.                 ibmacts(item_id);
  1911.                 break;
  1912.             }
  1913.         }
  1914.         if (quitdialog)
  1915.             break;
  1916.     }
  1917.     SetPort(oport);
  1918.     ZeroScrap();
  1919.     TEToScrap();
  1920.         /* copy the TE scrap to the Scrap */
  1921.         
  1922.     ptoc(macroname);
  1923.         
  1924.     CloseDialog(macrodp);
  1925.     SetDAFont(systemFont);    
  1926.  
  1927.     DetachResource(hmacro);
  1928.     closeconfig(keydp);
  1929.     return(hmacro);
  1930. }
  1931.  
  1932.  
  1933. pascal Boolean
  1934. Dsetmacro(dptr, devt, item)
  1935. DialogPtr dptr;
  1936. EventRecord * devt;
  1937. short * item;
  1938. {
  1939.     short nkeycode;                /* key code */
  1940.  
  1941.     bkrd_service();
  1942.     if (devt->what == keyDown) {
  1943.         nkeycode = (devt->message & 0xff00) >> 8;
  1944.  
  1945.         if (!(devt->modifiers & shiftKey) && !(devt->modifiers & cmdKey)
  1946.             && ((devt->modifiers & optionKey && !(devt->modifiers & ctrlKey))
  1947.                 || (devt->modifiers & ctrlKey && !(devt->modifiers & optionKey)))
  1948.             ) {
  1949.                 /* the Control or Option key is down all by itself */
  1950.                 /* make a control char */
  1951.             char contchar;
  1952.  
  1953.             contchar = (char) (devt->message & 0xff);
  1954.             if (!makecontrol(&contchar, nkeycode)) {
  1955.                 /* interpret as a control key */
  1956.                 return(TRUE);
  1957.             }
  1958.             *item = DKEYACTION;    /* show we saw a key entered */
  1959.             /* we courageously insert the char ourselves, since TE/Dialog won't reproduce correctly */
  1960.             TEDelete(macrodlog.textH);
  1961.                 /* kill select range as in standard */
  1962.             TEInsert(&contchar, (long) 1, macrodlog.textH);
  1963.             return(TRUE);
  1964.         }
  1965. #define MACROEDIT
  1966. #ifdef MACROEDIT
  1967.         if (devt->modifiers & cmdKey) {
  1968.             /* do Cut Copy and Paste? */
  1969.             char thechar;
  1970.             
  1971.             thechar = devt->message & 0xff;
  1972.             switch (thechar) {
  1973.                 case 'x': {
  1974.                     TECut(macrodlog.textH);
  1975.                     break;
  1976.                 }
  1977.                 case 'c': {
  1978.                     TECopy(macrodlog.textH);
  1979.                     break;
  1980.                 }
  1981.                 case 'v': {
  1982.                     TEPaste(macrodlog.textH);
  1983.                     break;
  1984.                 }
  1985.                 case '.': {
  1986.                     /* cancel setting the key */
  1987.                     *item = DKEYCANCEL;    
  1988.                     return(TRUE);
  1989.                 }
  1990.             }
  1991.             *item = DKEYACTION;    /* show we saw a key entered */
  1992.             return(TRUE);
  1993.         }
  1994. #endif
  1995.         /* TODO add test to distinguish CR & do a show button ? */
  1996.         
  1997.     }
  1998.     else if (devt->what == mouseDown && !KDnotshown) {
  1999.         /* pull down menus to insert codes if doing input and in menu area */
  2000.         short code;
  2001.         long menucode;
  2002.         WindowPtr whichWindow;
  2003.  
  2004.         code = FindWindow(pass(devt->where), &whichWindow);
  2005.         if (code == inMenuBar) {
  2006.             menucode = MenuSelect(pass(devt->where));
  2007.         }
  2008.     }
  2009.     return(FALSE);
  2010. }
  2011.  
  2012.  
  2013.